import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:jieli_ble_ota_plugin/jieli_ble_ota_plugin.dart';

import 'jieli_ble_ota_plugin_platform_interface.dart';

/// An implementation of [JieliBleOtaPluginPlatform] that uses method channels.
class MethodChannelJieliBleOtaPlugin extends JieliBleOtaPluginPlatform {
  /// The method channel used to interact with the native platform.
  @visibleForTesting
  final methodChannel = const MethodChannel('jieli_ble_ota_plugin');

  DataWriteHandler? _dataWriteHandler;
  OtaCallback? _otaCallback;
  LinkLayerHandler? _linkLayerHandler;

  MethodChannelJieliBleOtaPlugin() : super() {
    methodChannel.setMethodCallHandler((call) async {
      switch (call.method) {
        case JieliBleOtaPlugin.writeData:
          var bytes = call.arguments['bytes'];
          writeData(bytes);
          break;
        case JieliBleOtaPlugin.onOtaUpdateSuccess:
          onOtaUpdateSuccess();
          break;
        case JieliBleOtaPlugin.onOtaUpdateFailed:
          String code = call.arguments['error_code'];
          if (!code.contains("|")) {
            // iOS 错误码为整数，Android 错误码为 3 个整数，用 "|" 分隔；此处统一处理为 Android 格式
            code = "$code|0|0";
          }
          onOtaUpdateFailed(code);
          break;
        case JieliBleOtaPlugin.onOtaProgressChanged:
          var type = call.arguments['type'];
          var progress = call.arguments['progress'];
          onOtaUpdateProgressChanged(type, progress);
          break;
        case JieliBleOtaPlugin.onDeviceFirmwareStatusRequested:
          bool isFirmwareBroken = call.arguments['is_firmware_broken'];
          onDeviceFirmwareStatusRequested(true, isFirmwareBroken);
          break;
        case JieliBleOtaPlugin.onDeviceFirmwareStatusRequestFailed:
          int code = call.arguments['error_code'];
          onDeviceFirmwareStatusRequested(false, false);
          break;
        case JieliBleOtaPlugin.onRequestConnectLink:
          _linkLayerHandler!.reconnect();
          break;
        case JieliBleOtaPlugin.onRequestDisconnectLink:
          _linkLayerHandler!.disconnect();
          break;
      }
    });
  }

  @override
  Future writeData(Uint8List bytes) {
    if (_dataWriteHandler == null) throw Exception("You must call [setDataWriteHandler] first!");
    return _dataWriteHandler!.writeBytes(bytes);
  }

  @override
  void onOtaUpdateFailed(String code) {
    if (_otaCallback == null) throw Exception("You must call [setOtaCallback] first!");
    _otaCallback!.onOtaUpdateFailed(code);
  }

  @override
  void onOtaUpdateSuccess() {
    if (_otaCallback == null) throw Exception("You must call [setOtaCallback] first!");
    _otaCallback!.onOtaUpdateSuccess();
  }

  @override
  void onOtaUpdateProgressChanged(int type, double progress) {
    if (_otaCallback == null) throw Exception("You must call [setOtaCallback] first!");
    _otaCallback!.onOtaProgressChanged(type, progress);
  }

  @override
  void onDeviceFirmwareStatusRequested(bool success, bool isFirmwareBroken) {
    if (_otaCallback == null) throw Exception("You must call [setOtaCallback] first!");
    return _otaCallback!.onDeviceFirmwareStatusRequested(success, isFirmwareBroken);
  }

  @override
  void setDataWriteHandler(DataWriteHandler dataWriteHandler) {
    _dataWriteHandler = dataWriteHandler;
  }

  @override
  Future notifyDataReceived(Uint8List bytes) {
    return callNative(JieliBleOtaPlugin.methodNotifyDataReceived, bytes);
  }

  @override
  Future notifyLinkDisconnected(String identifier, String name) {
    return callNative(JieliBleOtaPlugin.methodNotifyDisconnected, {'identifier': identifier, 'name': name});
  }

  @override
  Future notifyLinkConnected(String identifier, String name) {
    return callNative(JieliBleOtaPlugin.methodNotifyConnected, {'identifier': identifier, 'name': name});
  }

  @override
  Future startOta(String file) {
    return callNative(JieliBleOtaPlugin.methodStart2Ota, file);
  }

  @override
  Future requestDeviceFirmwareState() {
    return callNative(JieliBleOtaPlugin.methodRequestDeviceFirmwareState);
  }

  @override
  void setOtaCallback(OtaCallback otaCallback) {
    _otaCallback = otaCallback;
  }

  @override
  void setLinkLayerHandler(LinkLayerHandler handler) {
    _linkLayerHandler = handler;
  }

  Future callNative(String method, [dynamic args]) {
    return methodChannel.invokeMethod(method, args);
  }
}
